import sys
import os
import json
import threading
import time
import wave
import sounddevice as sd
import numpy as np
import requests

# 设置Qt插件路径 - 解决Windows平台插件问题
def setup_qt_plugins():
    """设置Qt插件路径"""
    try:
        import PyQt5
        from pathlib import Path
        pyqt5_path = Path(PyQt5.__file__).parent

        # 尝试多个可能的插件路径
        possible_paths = [
            pyqt5_path / "Qt5" / "plugins",
            pyqt5_path / "Qt" / "plugins",
            pyqt5_path / "plugins"
        ]

        for plugin_path in possible_paths:
            if plugin_path.exists():
                os.environ['QT_PLUGIN_PATH'] = str(plugin_path)
                print(f"设置QT_PLUGIN_PATH = {plugin_path}")
                break
    except Exception as e:
        print(f"设置Qt插件路径时出错: {e}")

# 在导入PyQt5之前设置插件路径
setup_qt_plugins()

from PyQt5.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout,
                             QPushButton, QTextEdit, QLabel, QWidget, QProgressBar,
                             QMessageBox, QFileDialog, QFrame, QComboBox, QSpinBox,
                             QGroupBox, QGridLayout, QCheckBox, QListWidget, QListWidgetItem,
                             QTabWidget, QLineEdit, QTextBrowser)
from PyQt5.QtCore import QThread, pyqtSignal, QTimer, Qt
from PyQt5.QtGui import QFont, QIcon

# 语音合成模块 - 内置实现
import tempfile
import threading
from pathlib import Path
from config_manager import ConfigManager

class AudioRecorder(QThread):
    """音频录制线程 - 使用sounddevice"""
    finished = pyqtSignal(str)  # 录制完成信号，传递文件路径
    error = pyqtSignal(str)     # 错误信号

    def __init__(self, tmp_dir=None):
        super().__init__()
        self.is_recording = False
        self.tmp_dir = tmp_dir or Path("tmp")

        # 确保tmp目录存在
        self.tmp_dir.mkdir(exist_ok=True)

        # 生成带时间戳的录音文件名
        timestamp = time.strftime('%Y%m%d_%H%M%S')
        self.audio_file = str(self.tmp_dir / f"recording_{timestamp}.wav")

        # 音频参数
        self.channels = 1
        self.rate = 16000
        self.audio_data = []

    def start_recording(self):
        """开始录音"""
        # 每次录音生成新的文件名
        timestamp = time.strftime('%Y%m%d_%H%M%S')
        self.audio_file = str(self.tmp_dir / f"recording_{timestamp}.wav")

        self.is_recording = True
        self.audio_data = []
        self.start()

    def stop_recording(self):
        """停止录音"""
        self.is_recording = False

    def run(self):
        """录音线程主函数"""
        try:
            def audio_callback(indata, frames, time, status):
                """音频回调函数"""
                if self.is_recording and status.input_underflow == False:
                    self.audio_data.append(indata.copy())

            # 开始录音
            with sd.InputStream(callback=audio_callback,
                              channels=self.channels,
                              samplerate=self.rate,
                              dtype=np.float32):
                while self.is_recording:
                    sd.sleep(100)  # 100ms间隔检查

            # 保存音频文件
            if self.audio_data:
                # 合并音频数据
                audio_array = np.concatenate(self.audio_data, axis=0)

                # 转换为16位整数
                audio_int16 = (audio_array * 32767).astype(np.int16)

                # 保存为WAV文件
                with wave.open(self.audio_file, 'wb') as wf:
                    wf.setnchannels(self.channels)
                    wf.setsampwidth(2)  # 16-bit
                    wf.setframerate(self.rate)
                    wf.writeframes(audio_int16.tobytes())

                self.finished.emit(self.audio_file)
            else:
                self.error.emit("没有录制到音频数据")

        except Exception as e:
            self.error.emit(f"录音错误: {str(e)}")

class VoiceRecognitionAPI(QThread):
    """语音识别API调用线程 - 使用SiliconFlow API"""
    result = pyqtSignal(str)    # 识别结果信号
    error = pyqtSignal(str)     # 错误信号

    def __init__(self, audio_file, api_token="<token>"):
        super().__init__()
        self.audio_file = audio_file
        self.API_URL = "https://api.siliconflow.cn/v1/audio/transcriptions"
        self.API_TOKEN = api_token

    def run(self):
        """API调用主函数"""
        try:
            headers = {
                "Authorization": f"Bearer {self.API_TOKEN}",
                "Content-Type": "multipart/form-data"
            }

            # 准备multipart数据，按照你提供的格式
            with open(self.audio_file, 'rb') as audio_file:
                # 使用requests的files参数自动处理multipart
                files = {
                    'model': (None, 'FunAudioLLM/SenseVoiceSmall'),
                    'file': (os.path.basename(self.audio_file), audio_file, 'audio/wav')
                }

                # 移除Content-Type让requests自动设置
                headers_without_content_type = {
                    "Authorization": f"Bearer {self.API_TOKEN}"
                }

                response = requests.post(self.API_URL, headers=headers_without_content_type, files=files, timeout=30)

                if response.status_code == 200:
                    result = response.json()
                    if 'text' in result:
                        self.result.emit(result['text'])
                    elif 'transcription' in result:
                        self.result.emit(result['transcription'])
                    elif 'data' in result and 'text' in result['data']:
                        self.result.emit(result['data']['text'])
                    else:
                        # 如果返回格式不同，显示完整结果
                        self.result.emit(f"识别结果: {json.dumps(result, ensure_ascii=False, indent=2)}")
                else:
                    self.error.emit(f"API调用失败: {response.status_code} - {response.text}")

        except Exception as e:
            self.error.emit(f"API调用错误: {str(e)}")

class CustomVoiceAPI(QThread):
    """自定义音色管理API"""
    result = pyqtSignal(dict)    # 成功信号
    error = pyqtSignal(str)      # 错误信号
    progress = pyqtSignal(str)   # 进度信号

    def __init__(self, action, api_token, **kwargs):
        super().__init__()
        self.action = action  # upload, list, delete
        self.api_token = api_token
        self.kwargs = kwargs
        self.base_url = "https://api.siliconflow.cn/v1"

    def run(self):
        """执行API调用"""
        try:
            if self.action == "upload":
                self._upload_voice()
            elif self.action == "list":
                self._list_voices()
            elif self.action == "delete":
                self._delete_voice()
        except Exception as e:
            self.error.emit(f"API调用错误: {str(e)}")

    def _upload_voice(self):
        """上传自定义音色"""
        self.progress.emit("正在上传音色...")

        url = f"{self.base_url}/uploads/audio/voice"
        headers = {"Authorization": f"Bearer {self.api_token}"}

        audio_file = self.kwargs.get('audio_file')
        custom_name = self.kwargs.get('custom_name')
        text = self.kwargs.get('text')
        model = self.kwargs.get('model', 'FunAudioLLM/CosyVoice2-0.5B')

        if not all([audio_file, custom_name, text]):
            self.error.emit("缺少必要参数：音频文件、自定义名称或文本")
            return

        try:
            with open(audio_file, 'rb') as f:
                files = {'file': (os.path.basename(audio_file), f, 'audio/wav')}
                data = {
                    'model': model,
                    'customName': custom_name,
                    'text': text
                }

                response = requests.post(url, headers=headers, files=files, data=data, timeout=60)

                if response.status_code == 200:
                    result = response.json()
                    self.progress.emit("音色上传成功")
                    self.result.emit(result)
                else:
                    self.error.emit(f"上传失败: {response.status_code} - {response.text}")

        except Exception as e:
            self.error.emit(f"上传音色时出错: {str(e)}")

    def _list_voices(self):
        """获取音色列表"""
        self.progress.emit("正在获取音色列表...")

        url = f"{self.base_url}/audio/voice/list"
        headers = {"Authorization": f"Bearer {self.api_token}"}

        try:
            response = requests.get(url, headers=headers, timeout=30)

            if response.status_code == 200:
                result = response.json()
                self.progress.emit("音色列表获取成功")
                self.result.emit(result)
            else:
                self.error.emit(f"获取列表失败: {response.status_code} - {response.text}")

        except Exception as e:
            self.error.emit(f"获取音色列表时出错: {str(e)}")

    def _delete_voice(self):
        """删除音色"""
        uri = self.kwargs.get('uri')
        if not uri:
            self.error.emit("缺少音色URI")
            return

        self.progress.emit("正在删除音色...")

        url = f"{self.base_url}/audio/voice/deletions"
        headers = {
            "Authorization": f"Bearer {self.api_token}",
            "Content-Type": "application/json"
        }

        try:
            payload = {"uri": uri}
            response = requests.post(url, headers=headers, json=payload, timeout=30)

            if response.status_code == 200:
                self.progress.emit("音色删除成功")
                self.result.emit({"success": True, "message": "删除成功"})
            else:
                self.error.emit(f"删除失败: {response.status_code} - {response.text}")

        except Exception as e:
            self.error.emit(f"删除音色时出错: {str(e)}")

class VoiceRecognitionApp(QMainWindow):
    """主应用程序窗口"""
    
    def __init__(self):
        super().__init__()
        self.recorder = None
        self.api_thread = None
        self.recording_timer = QTimer()
        self.recording_time = 0

        # 初始化配置管理器
        self.config_manager = ConfigManager()

        # 创建录音文件保存目录
        self.tmp_dir = Path("tmp")
        self.tmp_dir.mkdir(exist_ok=True)

        # 加载API配置
        api_config = self.config_manager.load_api_config()
        self.siliconflow_token = api_config.get("siliconflow_token")

        # 初始化语音合成管理器
        self.tts_manager = TextToSpeechManager()
        # 设置API Token
        self.tts_manager.api_token = self.siliconflow_token

        # 自定义音色相关
        self.custom_voices = {}  # 存储自定义音色信息
        self.selected_audio_file = None  # 选中的音频文件路径
        self.custom_voice_api = None  # API调用线程

        # 音频下载相关
        self.current_audio_file = None  # 当前朗读的音频文件路径

        # 创建音频输出缓存目录
        self.output_dir = Path("tmp/output")
        self.output_dir.mkdir(parents=True, exist_ok=True)

        self.init_ui()
        self.setup_connections()

        # 启动时加载本地自定义音色列表
        self.load_local_custom_voices()

        # 启动时自动同步远程自定义音色列表
        QTimer.singleShot(2000, self.refresh_custom_voices)  # 延迟2秒同步远程
        
    def init_ui(self):
        """初始化用户界面"""
        self.setWindowTitle("语音识别程序")
        self.setGeometry(100, 100, 600, 500)
        
        # 创建中央widget
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        
        # 主布局
        layout = QVBoxLayout(central_widget)
        
        # 标题
        title_label = QLabel("语音识别与朗读程序")
        title_label.setAlignment(Qt.AlignCenter)
        title_label.setFont(QFont("Arial", 16, QFont.Bold))
        layout.addWidget(title_label)

        # API配置区域
        api_group = QGroupBox("API配置")
        api_layout = QHBoxLayout(api_group)

        api_layout.addWidget(QLabel("SiliconFlow Token:"))
        self.token_input = QTextEdit()
        self.token_input.setMaximumHeight(60)
        self.token_input.setPlaceholderText("请输入你的SiliconFlow API Token...")
        self.token_input.setText(self.siliconflow_token)
        api_layout.addWidget(self.token_input)

        self.save_token_btn = QPushButton("保存Token")
        self.save_token_btn.setStyleSheet("QPushButton { background-color: #4CAF50; color: white; }")
        api_layout.addWidget(self.save_token_btn)

        layout.addWidget(api_group)
        
        # 分隔线
        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
        layout.addWidget(line)
        
        # 录音控制区域
        control_layout = QHBoxLayout()
        
        self.record_btn = QPushButton("开始录音")
        self.record_btn.setMinimumHeight(40)
        self.record_btn.setStyleSheet("QPushButton { background-color: #4CAF50; color: white; font-size: 14px; }")
        control_layout.addWidget(self.record_btn)
        
        self.stop_btn = QPushButton("停止录音")
        self.stop_btn.setMinimumHeight(40)
        self.stop_btn.setEnabled(False)
        self.stop_btn.setStyleSheet("QPushButton { background-color: #f44336; color: white; font-size: 14px; }")
        control_layout.addWidget(self.stop_btn)
        
        self.file_btn = QPushButton("选择音频文件")
        self.file_btn.setMinimumHeight(40)
        self.file_btn.setStyleSheet("QPushButton { background-color: #2196F3; color: white; font-size: 14px; }")
        control_layout.addWidget(self.file_btn)
        
        layout.addLayout(control_layout)
        
        # 状态显示
        self.status_label = QLabel("状态: 就绪")
        self.status_label.setStyleSheet("QLabel { color: #333; font-size: 12px; }")
        layout.addWidget(self.status_label)
        
        # 录音时间显示
        self.time_label = QLabel("录音时间: 00:00")
        self.time_label.setStyleSheet("QLabel { color: #666; font-size: 12px; }")
        layout.addWidget(self.time_label)
        
        # 进度条
        self.progress_bar = QProgressBar()
        self.progress_bar.setVisible(False)
        layout.addWidget(self.progress_bar)
        
        # 结果显示区域
        result_label = QLabel("识别结果:")
        result_label.setFont(QFont("Arial", 12, QFont.Bold))
        layout.addWidget(result_label)

        self.result_text = QTextEdit()
        self.result_text.setMinimumHeight(150)
        self.result_text.setPlaceholderText("识别结果将在这里显示...")
        layout.addWidget(self.result_text)

        # 语音朗读控制区域 - 使用Tab页面
        tts_tab_widget = QTabWidget()

        # 基础设置Tab
        basic_tab = QWidget()
        basic_layout = QGridLayout(basic_tab)

        # 自动朗读选项
        self.auto_speak_checkbox = QCheckBox("自动朗读识别结果")
        self.auto_speak_checkbox.setChecked(False)  # 默认关闭自动朗读
        basic_layout.addWidget(self.auto_speak_checkbox, 0, 0, 1, 2)

        # 语音音色选择
        basic_layout.addWidget(QLabel("语音音色:"), 1, 0)
        self.voice_combo = QComboBox()
        self.voice_combo.addItems([
            "沉稳男声(alex)", "低沉男声(benjamin)", "磁性男声(charles)", "欢快男声(david)",
            "沉稳女声(anna)", "激情女声(bella)", "温柔女声(claire)", "欢快女声(diana)"
        ])
        basic_layout.addWidget(self.voice_combo, 1, 1)

        # 语音语速
        basic_layout.addWidget(QLabel("语速:"), 2, 0)
        self.speed_spin = QSpinBox()
        self.speed_spin.setRange(1, 5)
        self.speed_spin.setValue(3)
        basic_layout.addWidget(self.speed_spin, 2, 1)

        # 语音音调
        basic_layout.addWidget(QLabel("音调:"), 3, 0)
        self.pitch_spin = QSpinBox()
        self.pitch_spin.setRange(1, 5)
        self.pitch_spin.setValue(3)
        basic_layout.addWidget(self.pitch_spin, 3, 1)

        tts_tab_widget.addTab(basic_tab, "基础设置")

        # 自定义音色Tab
        custom_tab = QWidget()
        custom_layout = QVBoxLayout(custom_tab)

        # 上传音色区域
        upload_group = QGroupBox("上传自定义音色")
        upload_layout = QGridLayout(upload_group)

        upload_layout.addWidget(QLabel("音色名称:"), 0, 0)
        self.voice_name_input = QLineEdit()
        self.voice_name_input.setPlaceholderText("输入英文音色名称（如：my_voice）...")
        upload_layout.addWidget(self.voice_name_input, 0, 1)

        upload_layout.addWidget(QLabel("参考文本:"), 1, 0)
        self.voice_text_input = QTextEdit()
        self.voice_text_input.setMaximumHeight(60)
        self.voice_text_input.setPlaceholderText("输入参考音频对应的文本内容...")
        upload_layout.addWidget(self.voice_text_input, 1, 1)

        upload_buttons_layout = QHBoxLayout()
        self.select_audio_btn = QPushButton("选择音频文件")
        self.select_audio_btn.setStyleSheet("QPushButton { background-color: #2196F3; color: white; }")
        upload_buttons_layout.addWidget(self.select_audio_btn)

        self.upload_voice_btn = QPushButton("上传音色")
        self.upload_voice_btn.setStyleSheet("QPushButton { background-color: #4CAF50; color: white; }")
        upload_buttons_layout.addWidget(self.upload_voice_btn)

        upload_layout.addLayout(upload_buttons_layout, 2, 0, 1, 2)
        custom_layout.addWidget(upload_group)

        # 音色管理区域
        manage_group = QGroupBox("音色管理")
        manage_layout = QVBoxLayout(manage_group)

        manage_buttons_layout = QHBoxLayout()
        self.refresh_voices_btn = QPushButton("刷新音色列表")
        self.refresh_voices_btn.setStyleSheet("QPushButton { background-color: #FF9800; color: white; }")
        manage_buttons_layout.addWidget(self.refresh_voices_btn)

        self.delete_voice_btn = QPushButton("删除选中音色")
        self.delete_voice_btn.setStyleSheet("QPushButton { background-color: #f44336; color: white; }")
        manage_buttons_layout.addWidget(self.delete_voice_btn)

        manage_layout.addLayout(manage_buttons_layout)

        # 音色列表
        self.custom_voices_list = QListWidget()
        self.custom_voices_list.setMaximumHeight(150)
        manage_layout.addWidget(self.custom_voices_list)

        custom_layout.addWidget(manage_group)

        tts_tab_widget.addTab(custom_tab, "自定义音色")

        layout.addWidget(tts_tab_widget)

        # 朗读控制按钮
        tts_control_layout = QHBoxLayout()

        self.speak_btn = QPushButton("朗读选中文本")
        self.speak_btn.setStyleSheet("QPushButton { background-color: #9C27B0; color: white; }")
        tts_control_layout.addWidget(self.speak_btn)

        self.speak_all_btn = QPushButton("朗读全部结果")
        self.speak_all_btn.setStyleSheet("QPushButton { background-color: #673AB7; color: white; }")
        tts_control_layout.addWidget(self.speak_all_btn)

        self.stop_speak_btn = QPushButton("停止朗读")
        self.stop_speak_btn.setStyleSheet("QPushButton { background-color: #795548; color: white; }")
        tts_control_layout.addWidget(self.stop_speak_btn)

        layout.addLayout(tts_control_layout)

        # 音频下载按钮（初始隐藏）
        download_layout = QHBoxLayout()

        self.download_current_btn = QPushButton("保存当前朗读音频")
        self.download_current_btn.setStyleSheet("QPushButton { background-color: #00BCD4; color: white; }")
        self.download_current_btn.setVisible(False)  # 初始隐藏
        download_layout.addWidget(self.download_current_btn)

        layout.addLayout(download_layout)

        # 清除按钮
        self.clear_btn = QPushButton("清除结果")
        self.clear_btn.setStyleSheet("QPushButton { background-color: #FF9800; color: white; }")
        layout.addWidget(self.clear_btn)
        
    def setup_connections(self):
        """设置信号连接"""
        self.record_btn.clicked.connect(self.start_recording)
        self.stop_btn.clicked.connect(self.stop_recording)
        self.file_btn.clicked.connect(self.select_audio_file)
        self.clear_btn.clicked.connect(self.clear_results)
        self.recording_timer.timeout.connect(self.update_recording_time)

        # 语音朗读相关连接
        self.speak_btn.clicked.connect(self.speak_selected_text)
        self.speak_all_btn.clicked.connect(self.speak_all_text)
        self.stop_speak_btn.clicked.connect(self.stop_speaking)
        self.voice_combo.currentTextChanged.connect(self.update_voice_settings)
        self.speed_spin.valueChanged.connect(self.update_voice_settings)
        self.pitch_spin.valueChanged.connect(self.update_voice_settings)

        # 音频下载相关连接
        self.download_current_btn.clicked.connect(self.download_current_audio)

        # API配置相关连接
        self.save_token_btn.clicked.connect(self.save_api_token)

        # 自定义音色相关连接
        self.select_audio_btn.clicked.connect(self.select_audio_for_voice)
        self.upload_voice_btn.clicked.connect(self.upload_custom_voice)
        self.refresh_voices_btn.clicked.connect(self.refresh_custom_voices)
        self.delete_voice_btn.clicked.connect(self.delete_custom_voice)
        self.custom_voices_list.itemClicked.connect(self.on_custom_voice_selected)
        
    def start_recording(self):
        """开始录音"""
        try:
            self.recorder = AudioRecorder(self.tmp_dir)
            self.recorder.finished.connect(self.on_recording_finished)
            self.recorder.error.connect(self.on_error)
            
            self.recorder.start_recording()
            
            # 更新UI状态
            self.record_btn.setEnabled(False)
            self.stop_btn.setEnabled(True)
            self.file_btn.setEnabled(False)
            self.status_label.setText("状态: 正在录音...")
            
            # 开始计时
            self.recording_time = 0
            self.recording_timer.start(1000)  # 每秒更新一次
            
        except Exception as e:
            self.show_error(f"启动录音失败: {str(e)}")
            
    def stop_recording(self):
        """停止录音"""
        if self.recorder:
            self.recorder.stop_recording()
            self.recording_timer.stop()
            
    def update_recording_time(self):
        """更新录音时间显示"""
        self.recording_time += 1
        minutes = self.recording_time // 60
        seconds = self.recording_time % 60
        self.time_label.setText(f"录音时间: {minutes:02d}:{seconds:02d}")
        
    def on_recording_finished(self, audio_file):
        """录音完成处理"""
        # 重置UI状态
        self.record_btn.setEnabled(True)
        self.stop_btn.setEnabled(False)
        self.file_btn.setEnabled(True)

        # 显示录音文件保存信息
        file_name = os.path.basename(audio_file)
        self.status_label.setText(f"状态: 录音已保存到 tmp/{file_name}，正在识别...")

        # 显示进度条
        self.progress_bar.setVisible(True)
        self.progress_bar.setRange(0, 0)  # 不确定进度

        # 调用API进行识别
        self.call_recognition_api(audio_file)
        
    def select_audio_file(self):
        """选择音频文件"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择音频文件", "", 
            "音频文件 (*.wav *.mp3 *.m4a *.flac *.ogg);;所有文件 (*)"
        )
        
        if file_path:
            self.status_label.setText("状态: 正在识别选择的文件...")
            self.progress_bar.setVisible(True)
            self.progress_bar.setRange(0, 0)
            self.call_recognition_api(file_path)
            
    def call_recognition_api(self, audio_file):
        """调用语音识别API"""
        self.api_thread = VoiceRecognitionAPI(audio_file, self.siliconflow_token)
        self.api_thread.result.connect(self.on_recognition_result)
        self.api_thread.error.connect(self.on_error)
        self.api_thread.start()
        
    def on_recognition_result(self, result):
        """处理识别结果"""
        timestamp = time.strftime('%H:%M:%S')
        self.result_text.append(f"[{timestamp}] {result}\n")
        self.status_label.setText("状态: 识别完成")
        self.progress_bar.setVisible(False)

        # 自动朗读识别结果
        if self.auto_speak_checkbox.isChecked() and result.strip():
            self.speak_text(result)
        
    def on_error(self, error_msg):
        """处理错误"""
        self.show_error(error_msg)
        self.status_label.setText("状态: 发生错误")
        self.progress_bar.setVisible(False)
        
        # 重置UI状态
        self.record_btn.setEnabled(True)
        self.stop_btn.setEnabled(False)
        self.file_btn.setEnabled(True)
        self.recording_timer.stop()
        
    def clear_results(self):
        """清除结果"""
        self.result_text.clear()
        self.status_label.setText("状态: 就绪")
        
    def show_error(self, message):
        """显示错误消息"""
        QMessageBox.critical(self, "错误", message)

    def update_voice_settings(self):
        """更新语音设置"""
        # 解析音色选择
        voice_text = self.voice_combo.currentText()

        # 检查是否是自定义音色
        if voice_text.startswith("自定义音色("):
            # 提取自定义音色名称
            custom_name = voice_text.replace("自定义音色(", "").replace(")", "")
            if custom_name in self.custom_voices:
                voice_uri = self.custom_voices[custom_name]['uri']
                self.tts_manager.set_voice_settings(
                    gender="custom",
                    pitch=self.pitch_spin.value(),
                    speed=self.speed_spin.value(),
                    voice_name=custom_name,
                    custom_voice_uri=voice_uri
                )
                return

        # 系统预置音色
        if "男声" in voice_text:
            gender = "male"
        else:
            gender = "female"

        # 提取音色名称
        if "alex" in voice_text:
            voice_name = "alex"
        elif "benjamin" in voice_text:
            voice_name = "benjamin"
        elif "charles" in voice_text:
            voice_name = "charles"
        elif "david" in voice_text:
            voice_name = "david"
        elif "anna" in voice_text:
            voice_name = "anna"
        elif "bella" in voice_text:
            voice_name = "bella"
        elif "claire" in voice_text:
            voice_name = "claire"
        elif "diana" in voice_text:
            voice_name = "diana"
        else:
            voice_name = "alex"  # 默认

        speed = self.speed_spin.value()
        pitch = self.pitch_spin.value()

        self.tts_manager.set_voice_settings(gender, pitch, speed, voice_name)

    def speak_text(self, text):
        """朗读文本"""
        if not text or not text.strip():
            return

        # 开始新的朗读时，清理旧的缓存音频
        self.clear_old_cached_audio()

        # 隐藏下载按钮（等待新音频生成）
        self.download_current_btn.setVisible(False)
        self.current_audio_file = None

        # 更新语音设置
        self.update_voice_settings()

        def on_tts_progress(message):
            self.status_label.setText(f"语音合成: {message}")

        def on_tts_result(message):
            self.status_label.setText("状态: 朗读中...")

        def on_tts_error(message):
            self.status_label.setText("状态: 语音合成失败")
            self.show_error(f"语音合成错误: {message}")

        def on_tts_finished(audio_path):
            """音频生成完成后的回调"""
            self.status_label.setText("状态: 音频已生成，正在播放...")
            # 将音频文件移动到缓存目录
            cached_audio_path = self.cache_audio_file(audio_path)
            if cached_audio_path:
                self.current_audio_file = cached_audio_path
                # 显示下载按钮（音频生成完成就可以下载）
                self.download_current_btn.setVisible(True)
                self.download_current_btn.setText("保存当前朗读音频")

                # 通知TTS管理器使用缓存后的文件路径进行播放
                self.tts_manager.play_cached_audio(cached_audio_path, on_tts_error)

        self.tts_manager.speak_text(text, on_tts_progress, on_tts_result, on_tts_error, on_tts_finished)

    def speak_selected_text(self):
        """朗读选中的文本"""
        cursor = self.result_text.textCursor()
        selected_text = cursor.selectedText()

        if selected_text:
            self.speak_text(selected_text)
        else:
            QMessageBox.information(self, "提示", "请先选中要朗读的文本")

    def speak_all_text(self):
        """朗读全部结果"""
        all_text = self.result_text.toPlainText()

        if all_text.strip():
            # 提取纯文本内容，去除时间戳
            lines = all_text.split('\n')
            content_lines = []

            for line in lines:
                if line.strip():
                    # 去除时间戳格式 [HH:MM:SS]
                    if line.startswith('[') and ']' in line:
                        content = line.split(']', 1)
                        if len(content) > 1:
                            content_lines.append(content[1].strip())
                    else:
                        content_lines.append(line.strip())

            if content_lines:
                text_to_speak = ' '.join(content_lines)
                self.speak_text(text_to_speak)
            else:
                QMessageBox.information(self, "提示", "没有可朗读的内容")
        else:
            QMessageBox.information(self, "提示", "没有识别结果可以朗读")

    def stop_speaking(self):
        """停止朗读"""
        self.tts_manager.stop_current_speech()
        self.status_label.setText("状态: 已停止朗读")
        # 如果有音频文件，保持下载按钮可用
        if self.current_audio_file and os.path.exists(self.current_audio_file):
            self.download_current_btn.setText("保存当前朗读音频")

    def save_api_token(self):
        """保存API Token"""
        token = self.token_input.toPlainText().strip()
        if token:
            # 保存到配置文件
            if self.config_manager.set_api_token(token):
                self.siliconflow_token = token
                # 同时更新语音合成的Token
                self.tts_manager.api_token = token
                QMessageBox.information(self, "成功", "API Token已保存到配置文件！\n语音识别和语音合成都将使用新Token。")
                self.status_label.setText("状态: API Token已更新并保存")
            else:
                QMessageBox.warning(self, "警告", "保存API Token失败")
        else:
            QMessageBox.warning(self, "警告", "请输入有效的API Token")

    # ==================== 自定义音色管理方法 ====================

    def load_local_custom_voices(self):
        """加载本地保存的自定义音色"""
        try:
            local_voices = self.config_manager.get_custom_voices()

            # 清空现有列表
            self.custom_voices_list.clear()
            self.custom_voices.clear()

            # 加载本地音色
            for voice_name, voice_info in local_voices.items():
                voice_uri = voice_info.get('uri', '')
                voice_text = voice_info.get('text', '')

                # 存储音色信息
                self.custom_voices[voice_name] = {
                    'uri': voice_uri,
                    'text': voice_text
                }

                # 添加到列表
                list_item = QListWidgetItem(f"{voice_name} - {voice_text[:30]}...")
                self.custom_voices_list.addItem(list_item)

            if local_voices:
                self.status_label.setText(f"状态: 已加载 {len(local_voices)} 个本地音色")
            else:
                list_item = QListWidgetItem("暂无自定义音色")
                self.custom_voices_list.addItem(list_item)

        except Exception as e:
            print(f"加载本地音色失败: {e}")
            list_item = QListWidgetItem("加载本地音色失败")
            self.custom_voices_list.addItem(list_item)

    def select_audio_for_voice(self):
        """选择用于自定义音色的音频文件"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择音频文件", "",
            "音频文件 (*.wav *.mp3 *.m4a *.flac *.ogg);;所有文件 (*)"
        )

        if file_path:
            self.selected_audio_file = file_path
            self.select_audio_btn.setText(f"已选择: {os.path.basename(file_path)}")
            self.select_audio_btn.setStyleSheet("QPushButton { background-color: #4CAF50; color: white; }")

    def upload_custom_voice(self):
        """上传自定义音色"""
        voice_name = self.voice_name_input.text().strip()
        voice_text = self.voice_text_input.toPlainText().strip()

        if not voice_name:
            QMessageBox.warning(self, "警告", "请输入音色名称")
            return

        # 检查音色名称是否为英文
        if not voice_name.replace('_', '').replace('-', '').isalnum() or not voice_name.isascii():
            QMessageBox.warning(self, "警告", "音色名称只能包含英文字母、数字、下划线和连字符\n例如：my_voice, voice1, test-voice")
            return

        if not voice_text:
            QMessageBox.warning(self, "警告", "请输入参考文本")
            return

        if not self.selected_audio_file:
            QMessageBox.warning(self, "警告", "请选择音频文件")
            return

        if not os.path.exists(self.selected_audio_file):
            QMessageBox.warning(self, "警告", "选择的音频文件不存在")
            return

        # 显示进度
        self.progress_bar.setVisible(True)
        self.progress_bar.setRange(0, 0)
        self.status_label.setText("状态: 正在上传自定义音色...")

        # 调用API上传
        self.custom_voice_api = CustomVoiceAPI(
            "upload",
            self.siliconflow_token,
            audio_file=self.selected_audio_file,
            custom_name=voice_name,
            text=voice_text
        )

        self.custom_voice_api.result.connect(self.on_voice_upload_success)
        self.custom_voice_api.error.connect(self.on_voice_api_error)
        self.custom_voice_api.progress.connect(self.on_voice_api_progress)
        self.custom_voice_api.start()

    def refresh_custom_voices(self):
        """刷新自定义音色列表"""
        self.progress_bar.setVisible(True)
        self.progress_bar.setRange(0, 0)
        self.status_label.setText("状态: 正在获取音色列表...")

        self.custom_voice_api = CustomVoiceAPI("list", self.siliconflow_token)
        self.custom_voice_api.result.connect(self.on_voice_list_success)
        self.custom_voice_api.error.connect(self.on_voice_api_error)
        self.custom_voice_api.progress.connect(self.on_voice_api_progress)
        self.custom_voice_api.start()

    def delete_custom_voice(self):
        """删除选中的自定义音色"""
        current_item = self.custom_voices_list.currentItem()
        if not current_item:
            QMessageBox.warning(self, "警告", "请选择要删除的音色")
            return

        voice_name = current_item.text().split(" - ")[0]
        if voice_name not in self.custom_voices:
            QMessageBox.warning(self, "警告", "找不到音色信息")
            return

        # 确认删除
        reply = QMessageBox.question(
            self, "确认删除",
            f"确定要删除音色 '{voice_name}' 吗？\n此操作不可撤销。",
            QMessageBox.Yes | QMessageBox.No
        )

        if reply == QMessageBox.Yes:
            voice_uri = self.custom_voices[voice_name]['uri']

            self.progress_bar.setVisible(True)
            self.progress_bar.setRange(0, 0)
            self.status_label.setText("状态: 正在删除音色...")

            self.custom_voice_api = CustomVoiceAPI("delete", self.siliconflow_token, uri=voice_uri)
            self.custom_voice_api.result.connect(self.on_voice_delete_success)
            self.custom_voice_api.error.connect(self.on_voice_api_error)
            self.custom_voice_api.progress.connect(self.on_voice_api_progress)
            self.custom_voice_api.start()

    def on_custom_voice_selected(self, item):
        """选择自定义音色"""
        voice_name = item.text().split(" - ")[0]
        if voice_name in self.custom_voices:
            # 更新音色选择下拉框
            self.voice_combo.addItem(f"自定义音色({voice_name})")
            self.voice_combo.setCurrentText(f"自定义音色({voice_name})")

    def on_voice_upload_success(self, result):
        """音色上传成功"""
        self.progress_bar.setVisible(False)
        self.status_label.setText("状态: 音色上传成功")

        # 获取上传的音色信息
        voice_name = self.voice_name_input.text().strip()
        voice_text = self.voice_text_input.toPlainText().strip()
        voice_uri = result.get('uri', '')

        # 保存到本地配置（包含音频备份）
        if voice_uri and self.config_manager.add_custom_voice(voice_name, voice_uri, voice_text, self.selected_audio_file):
            # 立即添加到本地列表
            self.custom_voices[voice_name] = {
                'uri': voice_uri,
                'text': voice_text
            }

            # 添加到UI列表
            list_item = QListWidgetItem(f"{voice_name} - {voice_text[:30]}...")
            self.custom_voices_list.addItem(list_item)

            # 如果列表中有"暂无自定义音色"项，则移除
            for i in range(self.custom_voices_list.count()):
                item = self.custom_voices_list.item(i)
                if item and item.text() == "暂无自定义音色":
                    self.custom_voices_list.takeItem(i)
                    break

        # 清空输入
        self.voice_name_input.clear()
        self.voice_text_input.clear()
        self.selected_audio_file = None
        self.select_audio_btn.setText("选择音频文件")
        self.select_audio_btn.setStyleSheet("QPushButton { background-color: #2196F3; color: white; }")

        QMessageBox.information(self, "成功", f"音色上传成功并已保存到本地！\n音色名称: {voice_name}\nURI: {voice_uri}")

        # 延迟刷新远程音色列表以同步
        QTimer.singleShot(1000, self.refresh_custom_voices)

    def on_voice_list_success(self, result):
        """获取音色列表成功"""
        self.progress_bar.setVisible(False)
        self.status_label.setText("状态: 远程音色列表同步成功")

        print(f"API返回数据: {result}")  # 调试信息

        # 解析音色列表 - 修复数据结构解析
        voices = []
        if isinstance(result, dict):
            # 尝试多种可能的数据结构
            if 'result' in result:
                voices = result['result']
            elif 'data' in result:
                voices = result['data']
            elif 'voices' in result:
                voices = result['voices']
            else:
                # 如果result本身就是列表
                if isinstance(result, list):
                    voices = result
        elif isinstance(result, list):
            voices = result

        print(f"解析到的音色列表: {voices}")  # 调试信息

        # 同步到本地配置文件
        if voices:
            self.config_manager.sync_voices_from_api(voices)

        # 重新加载本地音色列表（包含刚同步的数据）
        self.load_local_custom_voices()

        if voices:
            self.status_label.setText(f"状态: 已同步 {len(voices)} 个远程音色到本地")
        else:
            self.status_label.setText("状态: 远程暂无自定义音色")

    def on_voice_delete_success(self, result):
        """音色删除成功"""
        self.progress_bar.setVisible(False)
        self.status_label.setText("状态: 音色删除成功")

        # 获取当前选中的音色名称
        current_item = self.custom_voices_list.currentItem()
        if current_item:
            voice_name = current_item.text().split(" - ")[0]

            # 从本地配置中删除
            self.config_manager.remove_custom_voice(voice_name)

            # 从内存中删除
            if voice_name in self.custom_voices:
                del self.custom_voices[voice_name]

            # 从UI列表中删除
            row = self.custom_voices_list.currentRow()
            self.custom_voices_list.takeItem(row)

            # 如果没有音色了，显示提示
            if self.custom_voices_list.count() == 0:
                list_item = QListWidgetItem("暂无自定义音色")
                self.custom_voices_list.addItem(list_item)

        QMessageBox.information(self, "成功", "音色删除成功！")

        # 延迟刷新远程音色列表以同步
        QTimer.singleShot(1000, self.refresh_custom_voices)

    def on_voice_api_progress(self, message):
        """API进度更新"""
        self.status_label.setText(f"状态: {message}")

    def on_voice_api_error(self, error_msg):
        """API错误处理"""
        self.progress_bar.setVisible(False)
        self.status_label.setText("状态: 操作失败")
        QMessageBox.critical(self, "错误", f"操作失败：{error_msg}")

    # ==================== 音频缓存管理功能 ====================

    def clear_old_cached_audio(self):
        """清理旧的缓存音频文件"""
        try:
            # 清理output目录中的旧音频文件
            if self.output_dir.exists():
                for audio_file in self.output_dir.glob("cached_audio_*.wav"):
                    try:
                        audio_file.unlink()
                    except Exception as e:
                        print(f"删除缓存文件失败: {e}")
        except Exception as e:
            print(f"清理缓存目录失败: {e}")

    def cache_audio_file(self, temp_audio_path):
        """将临时音频文件移动到缓存目录"""
        try:
            if not os.path.exists(temp_audio_path):
                return None

            # 生成缓存文件名
            timestamp = time.strftime('%Y%m%d_%H%M%S')
            cached_filename = f"cached_audio_{timestamp}.wav"
            cached_path = self.output_dir / cached_filename

            # 复制文件到缓存目录
            import shutil
            shutil.copy2(temp_audio_path, cached_path)

            # 删除临时文件
            try:
                os.remove(temp_audio_path)
            except:
                pass

            return str(cached_path)

        except Exception as e:
            print(f"缓存音频文件失败: {e}")
            return temp_audio_path  # 返回原路径作为备用

    # ==================== 音频下载功能 ====================

    def download_current_audio(self):
        """保存当前朗读的音频文件"""
        if not self.current_audio_file or not os.path.exists(self.current_audio_file):
            QMessageBox.warning(self, "警告", "没有可保存的音频文件\n请先朗读一些文本")
            return

        # 选择保存位置
        timestamp = time.strftime('%Y%m%d_%H%M%S')
        default_filename = f"朗读音频_{timestamp}.wav"

        save_path, _ = QFileDialog.getSaveFileName(
            self, "保存音频文件", default_filename,
            "音频文件 (*.wav);;所有文件 (*)"
        )

        if save_path:
            try:
                import shutil
                shutil.copy2(self.current_audio_file, save_path)
                self.status_label.setText(f"状态: 音频已保存到 {os.path.basename(save_path)}")
                QMessageBox.information(self, "成功", f"音频文件已保存到:\n{save_path}")

            except Exception as e:
                self.status_label.setText("状态: 保存音频失败")
                QMessageBox.critical(self, "错误", f"保存音频文件失败:\n{str(e)}")
        else:
            self.status_label.setText("状态: 取消保存")
        
    def closeEvent(self, event):
        """关闭事件处理"""
        if self.recorder and self.recorder.is_recording:
            self.recorder.stop_recording()
        if self.api_thread and self.api_thread.isRunning():
            self.api_thread.terminate()
        # 停止语音合成
        if self.tts_manager:
            self.tts_manager.stop_current_speech()
        # 清理缓存的音频文件
        self.clear_old_cached_audio()
        event.accept()

# ==================== 语音合成模块 ====================

class TextToSpeechAPI(QThread):
    """文字转语音API调用线程 - 使用SiliconFlow API"""
    result = pyqtSignal(str)    # 成功信号，传递音频文件路径
    error = pyqtSignal(str)     # 错误信号
    progress = pyqtSignal(str)  # 进度信号

    def __init__(self, text, voice_settings=None, api_token="sk-zuhzlkznhhgengmmpbbynaqbmtxsuflfcwhyndqcgaepacbc"):
        super().__init__()
        self.text = text
        self.voice_settings = voice_settings or {}

        self.API_URL = "https://api.siliconflow.cn/v1/audio/speech"
        self.API_TOKEN = api_token
        self.headers = {
            "Authorization": f"Bearer {self.API_TOKEN}",
            "Content-Type": "application/json"
        }

    def run(self):
        """主执行函数"""
        try:
            self.progress.emit("正在进行语音合成...")

            # 根据语音设置选择音色
            voice_name = self.voice_settings.get("voice_name", "alex")
            custom_voice_uri = self.voice_settings.get("custom_voice_uri")

            # 准备请求参数 - 使用SiliconFlow正确格式
            payload = {
                "model": "FunAudioLLM/CosyVoice2-0.5B",
                "input": self.text,
                "response_format": "wav"
            }

            # 如果是自定义音色，使用URI；否则使用系统预置音色
            if custom_voice_uri:
                payload["voice"] = custom_voice_uri
                self.progress.emit(f"使用自定义音色: {voice_name}")
            else:
                payload["voice"] = f"FunAudioLLM/CosyVoice2-0.5B:{voice_name}"
                self.progress.emit(f"使用系统音色: {voice_name}")

            # 发送同步请求
            response = requests.post(self.API_URL, headers=self.headers, json=payload, timeout=30)

            if response.status_code == 200:
                # SiliconFlow返回音频数据
                self.progress.emit("语音合成完成，正在保存音频...")

                # 保存音频到临时文件（稍后会被移动到缓存目录）
                temp_dir = tempfile.gettempdir()
                temp_file = os.path.join(temp_dir, f"tts_temp_{int(time.time())}.wav")

                with open(temp_file, 'wb') as f:
                    f.write(response.content)

                self.progress.emit("音频文件已保存")
                self.result.emit(temp_file)  # 返回本地文件路径

            else:
                self.error.emit(f"API调用失败: {response.status_code} - {response.text}")

        except Exception as e:
            self.error.emit(f"语音合成错误: {str(e)}")

class AudioPlayer(QThread):
    """音频播放器"""
    finished = pyqtSignal()
    error = pyqtSignal(str)

    def __init__(self, audio_path):
        super().__init__()
        self.audio_path = audio_path

    def run(self):
        """播放音频"""
        try:
            self.play_audio_file()
        except Exception as e:
            self.error.emit(f"播放音频时出错: {str(e)}")

    def play_audio_file(self):
        """播放音频文件"""
        try:
            # 检查文件是否存在
            if not os.path.exists(self.audio_path):
                self.error.emit(f"音频文件不存在: {self.audio_path}")
                return

            # 使用系统默认播放器播放
            if os.name == 'nt':  # Windows
                os.startfile(self.audio_path)
            elif os.name == 'posix':  # macOS and Linux
                if os.uname().sysname == 'Darwin':  # macOS
                    os.system(f'open "{self.audio_path}"')
                else:  # Linux
                    os.system(f'xdg-open "{self.audio_path}"')

            self.finished.emit()

            # 注意：不再自动删除音频文件，因为现在由缓存管理器处理

        except Exception as e:
            self.error.emit(f"播放音频失败: {str(e)}")

class TextToSpeechManager:
    """文字转语音管理器"""

    def __init__(self):
        self.current_tts_thread = None
        self.current_player_thread = None
        self.api_token = "sk-zuhzlkznhhgengmmpbbynaqbmtxsuflfcwhyndqcgaepacbc"
        self.voice_settings = {
            "gender": "male",  # male/female
            "pitch": 3,        # 1-5
            "speed": 3,        # 1-5
            "voice_name": "alex"
        }

    def set_voice_settings(self, gender="male", pitch=3, speed=3, voice_name="alex", custom_voice_uri=None):
        """设置语音参数"""
        self.voice_settings = {
            "gender": gender,
            "pitch": pitch,
            "speed": speed,
            "voice_name": voice_name,
            "custom_voice_uri": custom_voice_uri
        }

    def speak_text(self, text, progress_callback=None, result_callback=None, error_callback=None, finished_callback=None):
        """朗读文本"""
        if not text or not text.strip():
            if error_callback:
                error_callback("没有要朗读的文本")
            return

        # 停止当前的语音合成
        self.stop_current_speech()

        # 创建新的语音合成线程
        self.current_tts_thread = TextToSpeechAPI(text, self.voice_settings, self.api_token)

        # 连接信号
        if progress_callback:
            self.current_tts_thread.progress.connect(progress_callback)
        if error_callback:
            self.current_tts_thread.error.connect(error_callback)

        def on_tts_result(audio_path):
            """语音合成完成后的回调"""
            if result_callback:
                result_callback(f"语音合成完成: {audio_path}")

            # 音频生成完成，立即触发finished_callback（用于缓存和显示下载按钮）
            if finished_callback:
                finished_callback(audio_path)

            # 注意：不在这里播放音频，而是等待缓存完成后再播放

        self.current_tts_thread.result.connect(on_tts_result)
        self.current_tts_thread.start()

    def play_cached_audio(self, cached_audio_path, error_callback=None):
        """播放缓存的音频文件"""
        try:
            if not os.path.exists(cached_audio_path):
                if error_callback:
                    error_callback(f"缓存音频文件不存在: {cached_audio_path}")
                return

            # 播放缓存的音频
            self.current_player_thread = AudioPlayer(cached_audio_path)
            if error_callback:
                self.current_player_thread.error.connect(error_callback)

            self.current_player_thread.start()

        except Exception as e:
            if error_callback:
                error_callback(f"播放缓存音频失败: {str(e)}")

    def stop_current_speech(self):
        """停止当前的语音合成和播放"""
        if self.current_tts_thread and self.current_tts_thread.isRunning():
            self.current_tts_thread.terminate()
            self.current_tts_thread.wait()

        if self.current_player_thread and self.current_player_thread.isRunning():
            self.current_player_thread.terminate()
            self.current_player_thread.wait()

# ==================== 主程序入口 ====================

def main():
    app = QApplication(sys.argv)
    window = VoiceRecognitionApp()
    window.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()
